/*!
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *
 *    Author: flareguner
 *    E-Mail: flareguner@gmail.com
 *    Years: 2010-2010
 */







#include "mainwindow.h"
#include "ui_mainwindow.h"

#include "trayicon.h"
#include "settings.h"
#include "logger.h"
#include "wordsreservation.h"
#include "tooltip.h"
#include "pronounce.h"
#include "translate.h"
#include "hackedtextedit.h"
#include "about.h"
#include "defines.h"

#include <QtGui/QFileDialog>
#include <QtGui/QMessageBox>
#include <QtGui/QClipboard>
#include <QtGui/QCloseEvent>
#include <QtGui/QPrintDialog>
#include <QtGui/QPrinter>
#include <QtCore/QSettings>
#include <QtCore/QDate>
#include <QTranslator>
#include <QtCore/QThread>
#include "3rdparty/qxtshortcut/qxtglobalshortcut.h"

// Used by keyboard modifier check routine.
#ifdef Q_WS_X11
#   include <QX11Info>
#   include <X11/XKBlib.h>
#elif defined(Q_WS_WIN)
#   include <windows.h>
#   include <winuser.h>
#elif defined(Q_WS_MAC)
#   include <QApplication>
#endif


static const int selection_timeout = 500;



MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    m_tray_icon(new TrayIcon(this)),
    m_settings(new Settings(this)),
    m_logger(new Logger(this)),
    m_words_reservator(new WordsReservation(this)),
    m_tooltip(new ToolTip(this)),
    m_pronounce(new Pronounce(this)),
    m_translate(new Translate(this)),
    m_hacked_textEdit(new HackedTextEdit(this)),
    m_about(new About(this)),
    m_latest_save_path(""),
    m_translation_shortcut(new QxtGlobalShortcut(this)),
    m_clipboard(qApp->clipboard()),
    m_current_translator(0),
    m_selection_translate_timer(this),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ui->source_groupBox->layout()->addWidget(m_hacked_textEdit);

    m_selection_translate_timer.setSingleShot(true);

    const QStringList code_list = m_translate->getCodes();
    const QStringList langs_list  = m_translate->getNames();

    for(int i = 0; i < code_list.size(); i++) {
        const QString icon = QString(":/flags/artwork/flags/%1.png").
                arg(code_list.at(i));
        const QString name = langs_list.at(i);

        ui->source_language_comboBox->addItem(QIcon(icon), name);
        ui->result_language_comboBox->addItem(QIcon(icon), name);
    }

    //! Setup connections
    connect(ui->actionAuto_Translate, SIGNAL(toggled(bool)),m_settings,SLOT(setAutoTranslateChecked(bool)));
    connect(ui->actionScan_Selection,SIGNAL(toggled(bool)),m_settings,SLOT(setScanBufferChecked(bool)));
    connect(ui->actionAuto_Detect,SIGNAL(toggled(bool)),m_settings,SLOT(setAutoDetectChecked(bool)));

    //! Primary actions connections
    connect(ui->actionOpen, SIGNAL(triggered()), this, SLOT(open()));
    connect(ui->actionSave, SIGNAL(triggered()),this,SLOT(save()));
    connect(ui->actionSave_As, SIGNAL(triggered()),this,SLOT(saveAs()));
    connect(ui->actionTranslate, SIGNAL(triggered()),this,SLOT(translate()));
    connect(ui->actionExit, SIGNAL(triggered()),this,SLOT(exit()));

    connect(ui->actionClear, SIGNAL(triggered()),this,SLOT(clear()));
    connect(ui->actionCopy, SIGNAL(triggered()),this,SLOT(copy()));
    connect(ui->actionUndo, SIGNAL(triggered()),m_hacked_textEdit,SLOT(undo()));
    connect(ui->actionRedo, SIGNAL(triggered()),m_hacked_textEdit,SLOT(redo()));
    connect(ui->actionSwap, SIGNAL(triggered()),this,SLOT(swap()));

    connect(ui->actionSettings, SIGNAL(triggered()),this,SLOT(settings()));
    connect(ui->actionPronounce,SIGNAL(triggered()),this,SLOT(speech()));
    connect(ui->actionHistory, SIGNAL(triggered()),m_logger, SLOT(showLogs()));
    connect(ui->actionReserved_words, SIGNAL(triggered()),m_words_reservator, SLOT(showReservator()));

    connect(ui->actionAbout, SIGNAL(triggered()),m_about,SLOT(show()));
    connect(ui->actionAbout_Qt,SIGNAL(triggered()),this,SLOT(aboutQt()));



    connect(ui->switch_toolButton, SIGNAL(clicked()),ui->actionSwap,SLOT(trigger()));
    connect(ui->translate_apushButton, SIGNAL(clicked()),ui->actionTranslate,SLOT(trigger()));
    //! Text handling
    connect(m_hacked_textEdit,SIGNAL(textChanged()),this,SLOT(whenSourceTextChanged()));
    connect(ui->result_textBrowser,SIGNAL(textChanged()),this,SLOT(whenResultTextChanged()));
    connect(&m_selection_translate_timer, SIGNAL(timeout()), this, SLOT(slotTranslateSelectionTimeout()));

    connect(m_clipboard, SIGNAL(selectionChanged()),this,SLOT(whenSelectionChanged()));
    //! Settings
    connect(m_settings, SIGNAL(accepted()),this,SLOT(whenSettingsChanged()));

    //! Tray icon connections
    connect(m_tray_icon, SIGNAL(showOrHideRequested()),this,SLOT(showOrHide()));
    connect(m_tray_icon, SIGNAL(translationRequested()),ui->actionTranslate, SLOT(trigger()));
    connect(m_tray_icon, SIGNAL(swapRequested()),this,SLOT(swap()));
    connect(m_tray_icon, SIGNAL(scanSelectionChanged(bool)),ui->actionScan_Selection, SLOT(setChecked(bool)));
    connect(m_tray_icon, SIGNAL(configureRequested()),this, SLOT(settings()));
    connect(m_tray_icon, SIGNAL(quitRequested()), this, SLOT(exit()));

        connect(ui->actionScan_Selection, SIGNAL(toggled(bool)),m_tray_icon,SLOT(setScanBufferChecked(bool)));


    //! Speech


    //
    connect(m_hacked_textEdit, SIGNAL(undoAvailable(bool)),ui->actionUndo,SLOT(setEnabled(bool)));
    connect(m_hacked_textEdit, SIGNAL(redoAvailable(bool)),ui->actionRedo,SLOT(setEnabled(bool)));

    connect(m_translation_shortcut, SIGNAL(activated()),this,SLOT(whenTranslateHotkeyPressed()));

    connect(m_hacked_textEdit, SIGNAL(hasReserverdSequence(QString)),m_words_reservator,SLOT(addWord(QString)));
    connect(m_hacked_textEdit, SIGNAL(inputFinished()),this,SLOT(whenInputFinished()));
    connect(ui->actionPrint, SIGNAL(triggered()),this,SLOT(print()));
    whenSettingsChanged();
    readUiSettings();

    updateTrayTooltip();

}

MainWindow::~MainWindow()
{
    saveUiSettings();
    delete ui;
}

//---------------------------------------------------------------------------------------------------

void MainWindow::open() {
    const QString file_path = QFileDialog::getOpenFileName(this,
                                                           "", QDir::homePath(), "*.txt");

    QFile file(file_path);
    if(!file.exists()) return;

    if(file.open(QFile::Text | QFile::ReadOnly)) {
        m_hacked_textEdit->clear();
        m_hacked_textEdit->setPlainText(QString::fromUtf8(file.readAll().data()));
        file.close();
    }
}

void MainWindow::save() {
    QString path;

    if(QFile::exists(m_latest_save_path)) {
        path = m_latest_save_path;
    } else {
        path = QFileDialog::getSaveFileName(this,"", QDir::homePath(), "*.txt");
        m_latest_save_path = path;
    }
    QFile file(path);
    if(file.open(QFile::Text | QFile::Append)) {
        file.write(ui->result_textBrowser->toPlainText().toUtf8());
        file.close();
    }
}

void MainWindow::saveAs() {
    const QString path = QFileDialog::getSaveFileName(this, "",QDir::homePath(), "*.txt");
    QFile file(path);

    if(file.open(QFile::ReadWrite | QFile::Truncate | QFile::Text)) {
        file.write(ui->result_textBrowser->toPlainText().toUtf8());
        file.close();
        m_latest_save_path = path;
    }
}

void MainWindow::print() {
    QPrinter printer;
      QPrintDialog print_dialog(&printer);
      if(print_dialog.exec() == QPrintDialog::Accepted)
          ui->result_textBrowser->print(&printer);
}

void MainWindow::translate() {
    m_tooltip->setCursorPositionLocked(true);


    if(ui->actionAuto_Detect->isChecked()) {
        ui->source_language_comboBox->setCurrentIndex(
                    m_translate->getLanguageIndex(m_hacked_textEdit->toPlainText()));
    }

    const QStringList codes = m_translate->getCodes();
    const QStringList names = m_translate->getNames();
    const int src_index = ui->source_language_comboBox->currentIndex();
    const int dest_index = ui->result_language_comboBox->currentIndex();

    QString src_text = m_hacked_textEdit->toPlainText();
    const QString src = codes.at(src_index);
    const QString dest = codes.at(dest_index);


    if(src_text.isEmpty()) return;

    if(m_settings->getReservationEnabled()) {
        src_text = m_words_reservator->getProcessedText(src_text);
    }

    statusBar()->showMessage(tr("Translating...."));
    QString result = m_translate->translate(src,dest, src_text);

    if(m_settings->getReservationEnabled())
        result = m_words_reservator->getResultText(result);

    if(m_settings->getSimplifyResult())
        result = result.simplified();

    if(!result.isEmpty()) {
        statusBar()->showMessage(tr("Translated sucessfully!"));
        ui->result_textBrowser->setPlainText(result);
        notificate(result);

        if(m_settings->getAutoSpeachEnabled())
            speech();

    }
    else
        statusBar()->showMessage(tr("Translation error!"));

    m_tooltip->setCursorPositionLocked(false);


    if(m_settings->getHistoryEnabled()) {
        //Writing log
        QDate date = QDate::currentDate();
        const QString curdate  =date.toString("dd.MM.yyyy");
        QString logentry;
        logentry += QString("%1 | %2 -> %3 --------------------------------------------------------\n\n").arg(curdate, src, dest);
        logentry += (src_text + "\n\n");
        logentry += (result + "\n\n\n");
        m_logger->log(logentry);
    }
    updateTrayTooltip();

}

void MainWindow::exit() {
    saveUiSettings();
    qApp->quit();
}

//---------------------------------------------------------------------------------------------------

void MainWindow::clear() {
    m_hacked_textEdit->clear();
    ui->result_textBrowser->clear();
}

void MainWindow::copy() {
    const QString ttc = ui->result_textBrowser->toPlainText();
    qApp->clipboard()->setText(ttc);
}

void MainWindow::swap() {
    const int src_index = ui->source_language_comboBox->currentIndex();
    ui->source_language_comboBox->setCurrentIndex(ui->result_language_comboBox->currentIndex());
    ui->result_language_comboBox->setCurrentIndex(src_index);
}

//---------------------------------------------------------------------------------------------------

void MainWindow::settings() {
    if(isHidden())
        show();

    m_settings->show();
}

void MainWindow::speech() {
    const QString selected_text = ui->result_textBrowser->textCursor().selectedText();
    const QString text = ui->result_textBrowser->toPlainText();
    const int res_index = ui->result_language_comboBox->currentIndex();
    const QString language =m_translate->getCodes().at(res_index);

    if(!selected_text.isEmpty())
        m_pronounce->say(language, selected_text);
    else
        m_pronounce->say(language, text);
}

//---------------------------------------------------------------------------------------------------

void MainWindow::aboutQt() {
    QMessageBox::aboutQt(this);
}

//---------------------------------------------------------------------------------------------------

void MainWindow::whenSourceTextChanged() {
    ui->actionClear->setEnabled(!m_hacked_textEdit->toPlainText().isEmpty());
}

void MainWindow::whenResultTextChanged() {
    bool f = ui->result_textBrowser->toPlainText().isEmpty();
    ui->actionCopy->setEnabled(!f);
    ui->actionPronounce->setEnabled(!f);
}



void MainWindow::whenSettingsChanged() {
    ui->actionAuto_Translate->setChecked(m_settings->getAutoTranslate());
    ui->actionAuto_Detect->setChecked(m_settings->getAutoDetectLanguage());

    switch (m_settings->getTranslationTrigger()) {
        case Settings::SelectionBuffer:
        case Settings::ModifierPress:
            ui->actionScan_Selection->setChecked(true);
            break;
        default:
            ui->actionScan_Selection->setChecked(false);
    }

    if(m_settings->getShowInTray())
        m_tray_icon->show();
    else
        m_tray_icon->hide();

    ui->toolBar->setHidden(!m_settings->getShowToolbar());
    if(m_settings->getProxyEnabled()) {
        QNetworkProxy::setApplicationProxy(m_settings->getProxy());
    } else {
        QNetworkProxy tmp ;
        tmp.
                setType(QNetworkProxy::NoProxy);
        QNetworkProxy::setApplicationProxy(tmp);

    }

    if(m_settings->getTranslationTrigger() == Settings::HotkeyPress) {
        m_translation_shortcut->setShortcut(QKeySequence(m_settings->getTranslationHotkey()));
        m_translation_shortcut->setEnabled(true);
    } else if(m_settings->getTranslationTrigger() == Settings::SelectionBuffer) {
        ui->actionScan_Selection->setChecked(true);
    } else {
        m_translation_shortcut->setDisabled(true);
        ui->actionScan_Selection->setChecked(false);
    }
/// I have CHANGES!:)


    m_pronounce->setPronounceCmd(m_settings->getSpeachCmd());
    m_words_reservator->setCaseSensetive(m_settings->getReservationCaseSensetive());


    m_hacked_textEdit->setFont(m_settings->getFont());
    ui->result_textBrowser->setFont(m_settings->getFont());

    QString new_lang = m_settings->getLanguage();
    if (m_current_lang != new_lang) {
        QTranslator *new_trans = new QTranslator;
        if (new_trans->load(new_lang, qgt_l10n_dir)) {
            if (m_current_translator) {
                qApp->removeTranslator(m_current_translator);
                delete m_current_translator;
            }
            qApp->installTranslator(new_trans);
            m_current_translator = new_trans;
            m_current_lang = new_lang;
        }
    }
}

void MainWindow::whenSelectionChanged() {
    // No need selection in translator window
    if(this->isActiveWindow()) return;

    if(ui->actionScan_Selection->isChecked()) {
        m_selection_translate_timer.start(selection_timeout);
    }
}

void MainWindow::slotTranslateSelectionTimeout() {
    if (m_settings->getTranslationTrigger() == Settings::ModifierPress
            && !checkModifier(m_settings->getTranslationModifier()))
        return;

    m_hacked_textEdit->setPlainText(m_clipboard->text(QClipboard::Selection));
    ui->actionTranslate->trigger();
}

void MainWindow::whenTranslateHotkeyPressed() {
    m_hacked_textEdit->setText(m_clipboard->text(QClipboard::Selection));
    translate();
}

void MainWindow::whenInputFinished() {
if(ui->actionAuto_Translate->isChecked())
    translate();
}

void MainWindow::notificate(const QString &text) {
    Settings::NotificationMode m = m_settings->getNotificationMode();
    switch(m) {
        case Settings::ShowTrayMsg:
            m_tray_icon->notificate(tr("Translation"), text);
         break;
         case Settings::ShowToolTip:
            m_tooltip->showTooltipText(text);
         break;


        case Settings::ShowWindow:
            setFocus(Qt::ActiveWindowFocusReason);
        break;

         case Settings::JustTranslate:
         break;
    }
}

void MainWindow::showOrHide() {
    if(isHidden()) {
        m_tray_icon->setMainWindowHidden(false);
        show();
        setFocus(Qt::ActiveWindowFocusReason);
    } else {
        hide();
        m_tray_icon->setMainWindowHidden(true);
    }
}

void MainWindow::updateTrayTooltip() {
    QString info;
    info.append("QMyMemory " + qgt_version + "\n\n");
    info.append("Translation: " + ui->source_language_comboBox->currentText());
    info.append("  to  " + ui->result_language_comboBox->currentText());
    m_tray_icon->setToolTip(info);
}

//---------------------------------------------------------------------------------------------------

void MainWindow::readUiSettings() {
    QSettings settings(qgt_org, qgt_appname);
    settings.beginGroup("UI");
    const int src_index = settings.value("src_index", 0).toInt();
    const int dest_index = settings.value("dest_index", 0).toInt();
    const QSize winsize = settings.value("window_size", QSize(50, 50)).toSize();
    settings.endGroup();

    ui->source_language_comboBox->setCurrentIndex(src_index);
    ui->result_language_comboBox->setCurrentIndex(dest_index);
    resize(winsize);
}

void MainWindow::saveUiSettings() {
    const int src_index = ui->source_language_comboBox->currentIndex();
    const int dest_index = ui->result_language_comboBox->currentIndex();
    const QSize winsize = size();

    QSettings settings(qgt_org, qgt_appname);
    settings.beginGroup("UI");
    settings.setValue("src_index", src_index);
    settings.setValue("dest_index", dest_index);
    settings.setValue("window_size", winsize);
    settings.endGroup();
}

//---------------------------------------------------------------------------------------------------

void MainWindow::closeEvent(QCloseEvent *e) {
    if(m_settings->getShowInTray()) {
        e->ignore();
        hide();
        m_tray_icon->setMainWindowHidden(true);
    } else {
        e->accept();
        exit();
    }
}

void MainWindow::changeEvent(QEvent *e) {
    QMainWindow::changeEvent(e);
    switch (e->type()) {
        case QEvent::LanguageChange:
            ui->retranslateUi(this);
            m_tray_icon->retranslateMenu(this);
            break;
        default:
            break;
    }
}

// Original code is taken from QStarDict project.
bool MainWindow::checkModifier(Qt::KeyboardModifier mod) {
#ifdef Q_WS_X11
    XkbStateRec state;
    XkbGetState(QX11Info::display(), XkbUseCoreKbd, &state);
    switch (mod) {
        case Qt::AltModifier:
            return state.base_mods & 0010; // Alt
        case Qt::ShiftModifier:
            return state.base_mods & 0001; // Shift
        case Qt::ControlModifier:
            return state.base_mods & 0004; // Control
        case Qt::MetaModifier:
            return state.base_mods & 0100; // Meta
    }
    return false;
#elif defined(Q_WS_WIN)
    switch (mod) {
        case Qt::AltModifier:
            return GetAsyncKeyState(VK_MENU) & 0x8000;
        case Qt::ShiftModifier:
            return GetAsyncKeyState(VK_SHIFT) & 0x8000;
        case Qt::ControlModifier:
            return GetAsyncKeyState(VK_CONTROL) & 0x8000;
        case Qt::MetaModifier:
            return (GetAsyncKeyState(VK_LWIN) & 0x8000) || (GetAsyncKeyState(VK_RWIN) & 0x8000);
    }
    return false;
#elif defined(Q_WS_MAC)
    return QApplication::keyboardModifiers().testFlag(mod);
#endif
}



